﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _4Gewinnt
{
    /// <summary>
    /// Represents a game board at 4-gewinnt
    /// </summary>
    public class Board
    {
        /// <summary>
        /// Stores the actual game tokens
        /// </summary>
        private GameColor[,] grid;
        /// <summary>
        /// The number of rows of the board (readonly)
        /// </summary>
        public int Rows
        {
            get { return _rows; }
        }
        private int _rows = 6;

        /// <summary>
        /// The number of columns of the board (readonly)
        /// </summary>
        public int Cols
        {
            get { return _cols; }
        }
        private int _cols = 7;

        /// <summary>
        /// The number of consecutive token needed to win
        /// </summary>
        public int ttw
        {
            get { return _ttw; }
        }
        private int _ttw; 


        /// <summary>
        /// Initializes a new board
        /// </summary>
        /// <param name="ttw">The number of token needed to win (typically 4)</param>
        public Board(int ttw)
        {
            this._ttw = ttw;
            grid = new GameColor[_rows, _cols];
            for (int i = 0; i < _rows; i++)
            {
                for (int j = 0; j < _cols; j++)
                {
                    grid[i, j] = GameColor.Empty;
                }
            }
        }

        /// <summary>
        /// Initializes a new board
        /// </summary>
        /// <param name="ttw">The number of token needed to win (typically 4)</param>
        /// <param name="grid">The grid. gird gets copied during initialization</param>
        public Board(int ttw, GameColor[,] grid)
        {
            this._ttw = ttw;
            this.grid = new GameColor[_rows, _cols];
            for (int i = 0; i < _rows; i++)
            {
                for (int j = 0; j < _cols; j++)
                {
                    this.grid[i, j] = grid[i, j];
                }
            }
        }

        /// <summary>
        /// Returns an exact copy of this instance (all fields are copied)
        /// </summary>
        /// <returns>An exact copy of this instance (all fields are copied)</returns>
        public Board Clone()
        {
            return new Board(this._ttw, this.grid);
        }

        /// <summary>
        /// Finds a legal move in column <paramref name="c"/>. If there is no legal move in 
        /// <paramref name="c"/> (i.e. the column is full), -1 is returned
        /// </summary>
        /// <param name="c">The column to be searched for a legal move</param>
        /// <returns>The row number r such that (r,<paramref name="c"/>) is a legal move. 
        /// If there is no legal move in <paramref name="c"/>, -1 is returned.</returns>
        public int findLegalRowInColumn(int c)
        {
            //search rows from bottom to top for a free space
            for (int i = _rows - 1; i >= 0; i--)
            {
                if (isFieldFree(i, c))
                    return i;
            }

            //if no free space is found, return -1
            return -1;
        }


        /// <summary>
        /// Checks, whether board position (r,c) is free
        /// </summary>
        /// <param name="r">The row</param>
        /// <param name="c">The column</param>
        /// <returns>True if no token is on board position (r,c). False if out of bounds</returns>
        public bool isFieldFree(int r, int c)
        {
            //check obvious things
            if (r < 0 || r >= _rows || c < 0 || c >= _cols)
                return false;

            if (grid[r, c] == GameColor.Empty)
                return true;
            else
                return false;
        }



        /// <summary>
        /// Checks whether it is legal to move to column <paramref name="c"/>
        /// </summary>
        /// <param name="c">The column to move to</param>
        /// <returns>True, if it is legal to move to column <paramref name="c"/></returns>
        public bool isLegalColumn(int c)
        {
            if (grid[0, c] == GameColor.Empty)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Move a token to (r,c). An exception is thrown if move was illegal
        /// </summary>
        /// <param name="color">The color of the token to be moved</param>
        /// <param name="c">The column</param>
        public void moveTo(GameColor color, int c)
        {
            int r = findLegalRowInColumn(c);

            //check whether move is legal
            if (r == -1)
                throw new Exception("Illegal move: You cannot move to (" + r.ToString() + "," + c.ToString() + ")");
            
            //apply move
            grid[r, c] = color;
        }


        /// <summary>
        /// Returns which player is at (r,c)
        /// </summary>
        /// <param name="r">The row</param>
        /// <param name="c">The column</param>
        /// <returns>The player at (r,c)</returns>
        public GameColor playerAt(int r, int c)
        {
            return grid[r, c];
        }


        /// <summary>
        /// Returns true if there are no free spaces on the board
        /// </summary>
        /// <returns>True if there are no free spaces on the board</returns>
        public bool isBoardFull()
        {
            for (int i = 0; i < _rows; i++)
            {
                for (int j = 0; j < _cols; j++)
                {
                    if (grid[i, j] == GameColor.Empty)
                        return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Returns true if no token is on the field yet
        /// </summary>
        /// <returns>True if no token is on the field yet</returns>
        public bool isBoardFree()
        {
            for (int i = 0; i < _rows; i++)
            {
                for (int j = 0; j < _cols; j++)
                {
                    if (grid[i, j] != GameColor.Empty)
                        return false;
                }
            }
            return true;

        }


        /// <summary>
        /// Checks, whether <paramref name="color"/> has won
        /// </summary>
        /// <param name="color">The color</param>
        /// <returns>True if player has won</returns>
        public bool hasWon(GameColor color)
        {
            int[,] winningPosition;
            return hasWon(color, out winningPosition);
        }


        /// <summary>
        /// Checks, whether <paramref name="color"/> has won
        /// </summary>
        /// <param name="color">The color</param>
        /// <param name="winningPosition">The winning position as 4x2 Matrix</param>
        /// <returns>True if player has won</returns>
        public bool hasWon(GameColor color, out int[,] winningPosition)
        {
            //init winning Position
            winningPosition = new int[_ttw, 2];

            //the number of found continous tokens
            int nTokens;

            //search horizontal: -
            for (int r = 0; r < _rows; r++)
            {
                nTokens = 0;
                for (int c = 0; c < _cols; c++)
                {
                    //if token is found
                    if (grid[r, c] == color)
                    {
                        nTokens++;
                        winningPosition[nTokens - 1, 0] = r;
                        winningPosition[nTokens - 1, 1] = c;
                    }
                    else
                        nTokens = 0;
                    //if ttw tokens are found: the player has won, return true
                    if (nTokens == _ttw)
                        return true;
                }
            }

            //search vertical: |
            for (int c = 0; c < _cols; c++)
            {
                nTokens = 0;
                for (int r = 0; r < _rows; r++)
                {
                    //if token is found
                    if (grid[r, c] == color)
                    {
                        nTokens++;
                        winningPosition[nTokens - 1, 0] = r;
                        winningPosition[nTokens - 1, 1] = c;
                    }
                    else
                        nTokens = 0;
                    //if ttw tokens are found: the player has won, return true
                    if (nTokens == _ttw)
                        return true;
                }
            }

            //search diagonal: /
            int nDiags = _rows + _cols - 1;
            for (int d = _ttw - 1; d < nDiags - _ttw + 1; d++)
            {
                nTokens = 0;
                for (int j = 0; j <= d; j++)
                {
                    int r = j;
                    int c = d - r;

                    if (r >= 0 && r < _rows && c >= 0 && c < _cols)
                    {
                        //if token is found
                        if (grid[r, c] == color)
                        {
                            nTokens++;
                            winningPosition[nTokens - 1, 0] = r;
                            winningPosition[nTokens - 1, 1] = c;
                        }
                        else
                            nTokens = 0;
                        //if ttw tokens are found: the player has won, return true
                        if (nTokens == _ttw)
                            return true;
                    }
                }
            }


            //search diagonal: \
            for (int d = _ttw; d < nDiags - _ttw + 2; d++)
            {
                nTokens = 0;
                for (int j = 0; j <= d; j++)
                {
                    int r = j;
                    int c = _cols - (d - r);

                    if (r >= 0 && r < _rows && c >= 0 && c < _cols)
                    {
                        //if token is found
                        if (grid[r, c] == color)
                        {
                            nTokens++;
                            winningPosition[nTokens - 1, 0] = r;
                            winningPosition[nTokens - 1, 1] = c;
                        }
                        else
                            nTokens = 0;
                        //if ttw tokens are found: the player has won, return true
                        if (nTokens == _ttw)
                            return true;
                    }
                }
            }



            //return false if nothing is found
            return false;
        }



    }
}
